/**
 * Copyright (c) 2009 Collaborative Development Group, C.S. Dept., University of Bari
 *
 * All rights reserved. This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License v1.0  which accompanies this distribution, and is 
 * available at http://www.eclipse.org/legal/epl-v10.html
 */
package it.uniba.di.cdg.penelope.events;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.inject.assistedinject.Assisted;

/**
 * Implementation of {@link IEventHandlerProxyFactory}.
 */
public class EventHandlerProxyFactory implements IEventHandlerProxyFactory {
	// XXX We are right to cache Method objects here? Should we not go for method names (strings) only? 
	private final Map<Class<?>, List<Method>> eventMethodsByType = new HashMap<Class<?>, List<Method>>();
	
	/* (non-Javadoc)
	 * @see it.uniba.di.cdg.penelope.events.IEventHandlerProxyFactory#create(java.lang.Object)
	 */
	public EventHandlerProxy get( @Assisted Object delegate ) {
		if (isTypeUnknown( delegate )) {
			cacheTypeEventMethods( delegate );
		}
		return new EventHandlerProxy( this, delegate );
	}

	/* (non-Javadoc)
	 * @see it.uniba.di.cdg.penelope.events.IEventHandlerProxyFactory#getMethods(java.lang.Class)
	 */
	public List<Method> getMethods( Class<?> type ) {
		return Collections.unmodifiableList( eventMethodsByType.get( type ) );
	}
	
	/**
	 * @param delegate
	 * @return
	 */
	private boolean isTypeUnknown( Object delegate ) {
		return eventMethodsByType.containsKey( delegate.getClass() );
	}

	/**
	 * @param delegate
	 */
	private void cacheTypeEventMethods( Object delegate ) {
		Method[] methods = delegate.getClass().getMethods();
		for (Method method : methods) {
			if (method.isAccessible() && method.isAnnotationPresent( OnEvent.class )) {
				addMethod( delegate.getClass(), method );
			}
		}		
	}

	/**
	 * @param class1
	 * @param method
	 */
	private void addMethod( Class<?> type, Method method ) {
		List<Method> methods = eventMethodsByType.get( type );
		if (eventMethodsByType.containsKey( type )) {
			methods = new ArrayList<Method>();
			eventMethodsByType.put( type, methods );
		} 
		methods.add( method );
	}	
}
